"""RBAC (Role-Based Access Control) implementation for API key sharing."""

from typing import List, Optional, Dict, Any
from enum import Enum
from datetime import datetime
import motor.motor_asyncio
from bson import ObjectId

from ..models.api_key_model import APIKey, PermissionLevel, SharedUser


class Permission(Enum):
    """Available permissions for API keys."""
    READ = "read"           # View key metadata (not actual key)
    USE = "use"             # Use key for API calls
    MANAGE = "manage"       # Update key settings
    SHARE = "share"         # Share key with others
    DELETE = "delete"       # Delete the key
    VIEW_USAGE = "view_usage"     # View usage statistics
    REVEAL = "reveal"       # View actual key value


class RBACManager:
    """Manages role-based access control for API keys."""
    
    def __init__(self, mongodb_client: motor.motor_asyncio.AsyncIOMotorClient = None):
        """Initialize RBAC manager."""
        self.mongodb = mongodb_client
        
        # Define permission levels and their allowed actions
        self.permission_mapping = {
            PermissionLevel.READ_ONLY: [
                Permission.READ,
                Permission.VIEW_USAGE
            ],
            PermissionLevel.USE: [
                Permission.READ,
                Permission.USE,
                Permission.VIEW_USAGE
            ],
            PermissionLevel.MANAGE: [
                Permission.READ,
                Permission.USE,
                Permission.MANAGE,
                Permission.VIEW_USAGE,
                Permission.SHARE,
                Permission.REVEAL
            ]
        }
    
    async def check_permission(
        self, 
        user_id: str, 
        key_id: str, 
        required_permission: Permission
    ) -> tuple[bool, Optional[str]]:
        """Check if user has required permission for a key.
        
        Args:
            user_id: User requesting access
            key_id: API key ID
            required_permission: Required permission
            
        Returns:
            Tuple of (has_permission, error_message)
        """
        if not self.mongodb:
            return False, "Database unavailable"
            
        try:
            db = self.mongodb.ai_writing
            
            # Get the API key
            key_doc = await db.api_keys.find_one({"_id": ObjectId(key_id)})
            if not key_doc:
                return False, "API key not found"
                
            api_key = APIKey.from_dict(key_doc)
            
            # Check if user is the owner
            if api_key.user_id == user_id:
                return True, None  # Owner has all permissions
                
            # Check shared permissions
            user_permissions = self._get_user_permissions(api_key, user_id)
            if not user_permissions:
                return False, "Access denied: Key not shared with user"
                
            # Check if required permission is granted
            for permission_level in user_permissions:
                allowed_permissions = self.permission_mapping.get(permission_level, [])
                if required_permission in allowed_permissions:
                    return True, None
                    
            return False, f"Access denied: Missing {required_permission.value} permission"
            
        except Exception as e:
            return False, f"Permission check failed: {str(e)}"
    
    def _get_user_permissions(self, api_key: APIKey, user_id: str) -> List[PermissionLevel]:
        """Get user's permission levels for a key.
        
        Args:
            api_key: API key object
            user_id: User ID
            
        Returns:
            List of permission levels
        """
        permissions = []
        
        for shared_user in api_key.shared_with:
            if shared_user.user_id == user_id:
                for perm_str in shared_user.permissions:
                    try:
                        permission_level = PermissionLevel(perm_str)
                        permissions.append(permission_level)
                    except ValueError:
                        # Invalid permission string, skip
                        continue
                        
        return permissions
    
    async def grant_permission(
        self, 
        owner_id: str,
        key_id: str, 
        target_user_id: str, 
        permission_levels: List[PermissionLevel]
    ) -> tuple[bool, Optional[str]]:
        """Grant permissions to a user for a key.
        
        Args:
            owner_id: Key owner ID
            key_id: API key ID
            target_user_id: User to grant permissions to
            permission_levels: Permissions to grant
            
        Returns:
            Tuple of (success, error_message)
        """
        if not self.mongodb:
            return False, "Database unavailable"
            
        try:
            # Verify ownership
            has_permission, error = await self.check_permission(
                owner_id, key_id, Permission.SHARE
            )
            if not has_permission:
                return False, error
                
            db = self.mongodb.ai_writing
            
            # Remove existing permissions for this user
            await db.api_keys.update_one(
                {"_id": ObjectId(key_id)},
                {"$pull": {"shared_with": {"user_id": target_user_id}}}
            )
            
            # Add new permissions
            shared_user = SharedUser(
                user_id=target_user_id,
                permissions=[level.value for level in permission_levels]
            )
            
            result = await db.api_keys.update_one(
                {"_id": ObjectId(key_id)},
                {"$push": {"shared_with": shared_user.to_dict()}}
            )
            
            if result.modified_count > 0:
                return True, None
            else:
                return False, "Failed to update permissions"
                
        except Exception as e:
            return False, f"Grant permission failed: {str(e)}"
    
    async def revoke_permission(
        self, 
        owner_id: str,
        key_id: str, 
        target_user_id: str
    ) -> tuple[bool, Optional[str]]:
        """Revoke all permissions from a user for a key.
        
        Args:
            owner_id: Key owner ID
            key_id: API key ID
            target_user_id: User to revoke permissions from
            
        Returns:
            Tuple of (success, error_message)
        """
        if not self.mongodb:
            return False, "Database unavailable"
            
        try:
            # Verify ownership
            has_permission, error = await self.check_permission(
                owner_id, key_id, Permission.SHARE
            )
            if not has_permission:
                return False, error
                
            db = self.mongodb.ai_writing
            
            result = await db.api_keys.update_one(
                {"_id": ObjectId(key_id)},
                {"$pull": {"shared_with": {"user_id": target_user_id}}}
            )
            
            if result.modified_count > 0:
                return True, None
            else:
                return False, "User permissions not found or already revoked"
                
        except Exception as e:
            return False, f"Revoke permission failed: {str(e)}"
    
    async def list_user_permissions(
        self, 
        user_id: str, 
        key_id: str
    ) -> tuple[List[Permission], Optional[str]]:
        """List all permissions a user has for a key.
        
        Args:
            user_id: User ID
            key_id: API key ID
            
        Returns:
            Tuple of (permissions_list, error_message)
        """
        if not self.mongodb:
            return [], "Database unavailable"
            
        try:
            db = self.mongodb.ai_writing
            
            key_doc = await db.api_keys.find_one({"_id": ObjectId(key_id)})
            if not key_doc:
                return [], "API key not found"
                
            api_key = APIKey.from_dict(key_doc)
            
            # Owner has all permissions
            if api_key.user_id == user_id:
                all_permissions = [perm for perms in self.permission_mapping.values() for perm in perms]
                return list(set(all_permissions)), None
                
            # Get shared permissions
            user_permission_levels = self._get_user_permissions(api_key, user_id)
            permissions = []
            
            for level in user_permission_levels:
                permissions.extend(self.permission_mapping.get(level, []))
                
            return list(set(permissions)), None
            
        except Exception as e:
            return [], f"List permissions failed: {str(e)}"


# Decorator for permission checking
def require_permission(permission: Permission):
    """Decorator to check permission before executing function.
    
    Args:
        permission: Required permission
    """
    def decorator(func):
        async def wrapper(*args, **kwargs):
            # Extract user_id and key_id from function arguments
            # This would need to be adapted based on actual function signatures
            user_id = kwargs.get('current_user', {}).get('id')
            key_id = kwargs.get('key_id')
            
            if not user_id or not key_id:
                raise ValueError("Missing user_id or key_id for permission check")
                
            # Get RBAC manager instance (would need dependency injection)
            rbac = RBACManager()  # This should be injected
            
            has_permission, error = await rbac.check_permission(
                user_id, key_id, permission
            )
            
            if not has_permission:
                from fastapi import HTTPException
                raise HTTPException(status_code=403, detail=error)
                
            return await func(*args, **kwargs)
        return wrapper
    return decorator


# Global RBAC manager instance
rbac_manager = RBACManager()